﻿using ServerListerner.Connections;
using ServerListerner.Helper;

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;


namespace ServerListerner.Server
{
    /// <summary>
    /// Listen To TCP
    /// </summary>
    public class TCPServer : ServerConnect, ITCPServer
    {
        private readonly int _port;
        private readonly IPAddress _ipaddr;
        private readonly Socket _socket;
        private Action<ITCPConnection> _action;
        public bool IsRunning = false;
        private List<MpscChannel<bool>> mpscChannels = new List<MpscChannel<bool>>();
        public TCPServer(int port)
        {
            _port = port;
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }
        public TCPServer(IPAddress ipaddr, int port) : this(port)
        {
            _ipaddr = ipaddr;
        }
        public TCPServer(string ip, int port) : this(IPAddress.Parse(ip), port) { }
        // public bool StartListen(Action<T> action)
        public bool StartListen(Action<ITCPConnection> action)
        {
            try
            {
                if (_port.PortInUse())
                    return false;
                IsRunning = true;
                _action = action;
                IPAddress iPAddress = _ipaddr == null || _ipaddr == IPAddress.None ? IPAddress.Any : _ipaddr;
                _socket.Bind(new IPEndPoint(iPAddress, _port));
                _socket.Listen(int.MaxValue);
                if (!_port.PortInUse())
                    return false;
                StartTCPAccept();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }

        }
        public bool StopListen()
        {
            IsRunning = false;
            mpscChannels.ForEach(s => s.Send(true));
            CloseSocket();
            return !_port.PortInUse();
        }
        private void StartTCPAccept()
          => _socket.BeginAccept(TCPAccept, _socket);
        private void TCPAccept(IAsyncResult iar)
        {
            try
            {
                if (!IsRunning) return;
                if (iar.IsCompleted && iar.AsyncState != null)
                {
                    Socket client = _socket.EndAccept(iar);
                    if (client.Connected)
                    {
                        MpscChannel<bool> channel = new MpscChannel<bool>();
                        StartConnection<ITCPConnection>(new object[] { client, channel, _action });
                        //new TCPConnection(client, channel, _action);
                        mpscChannels.Add(channel);

                    }
                }
            }
            finally
            {
                if (IsRunning)
                    StartTCPAccept();
            }
        }
        public void Dispose()
        {
            StopListen();
            _action = null;
        }
        private void CloseSocket()
        {
            if (_socket.Connected)
                _socket.Shutdown(SocketShutdown.Both);
            _socket.Close();
            _socket.Dispose();
        }
    }
}
